﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml;
using AmiEmuLib;
using AmiEmuLib.AmigaConfiguration;
using AmiEmuLib.AmigaMachine;
using AmiEmuLib.AmigaMachine.AmigaMemory;
using AmiEmuLib.Configuration.AmigaEventsConfig;
using AmiEmuLib.Configuration.DefaultAmigaConfigsConfig;
using AmiEmuLib.Configuration.EventBindingConfig;
using AmiEmuLib.Configuration.FiltersConfig;
using AmiEmuLib.Configuration.FloppySoundConfig;
using AmiEmuLib.Configuration.KnownRomConfig;
using AmiEmuLib.Host;
using AmiEmuLib.Host.HostInput;
using AmiEmuLib.Native;
using AmiEmuLib.Render.Filter.ShaderFilter;
using AmiEmuLib.Render.Filter.SoftwareFilter;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using System.Diagnostics;
using AmiEmuLib.Configuration.AmigaMemoryMap;
using TomanuExtensions;
using TomanuExtensions.Utils;
using AmiEmuLib.Configuration.KeyboardConfig;
using AmiEmuLib.Configuration.AmigaAudioConfig;
using AmiEmuLib.AmigaConfiguration.ChipsetConfig;
using AmiEmuLib.Host.HostSound;
using AmiEmuLib.AmigaMachine.Audio;

namespace amiemu_lib_test
{
    [TestClass()]
    public class AmiEmuLibTest : TestBase
    {
        private TestContext m_test_context;

        public TestContext TestContext
        {
            get
            {
                return m_test_context;
            }
            set
            {
                m_test_context = value;
            }
        }

        [TestCategory("Configuration"), TestMethod]
        public void KnownRoms_Test()
        {
            Assert.IsTrue(KnownRoms.Roms.Count() == 102);
            Assert.IsTrue(KnownRoms.Roms.Where(kr => kr.Exists).Count() == 25);
        }

        [TestCategory("Configuration"), TestMethod]
        public void AmigaEvents_Test()
        {
            Assert.IsTrue(AmigaEventsList.Events.Count() == 9);

            Assert.IsTrue(AmigaEventsList.Events.First(
                aies => aies.DeviceType == AmigaDeviceType.Mouse).Events.First(
                aie => aie.Name == "Mouse Relative Move X").HideFromMapping);
            Assert.IsTrue(AmigaEventsList.Events.First(
                aies => aies.DeviceType == AmigaDeviceType.Mouse).Events.Count(
                aie => aie.HideFromMapping) == 4);

            var types = AmigaEventsList.Events.Select(aies => aies.DeviceType);
            Assert.IsTrue(types.Unique());
            Assert.IsTrue(EnumExtensions.GetValues<AmigaDeviceType>().Exact(types));

            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.Select(el => el.ID).Unique()));

            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.All(el => el.ID > 0)));

            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.Select(el => el.Name).Unique()));
            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.All(el => el.Name == el.Name.Trim())));
            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.All(el => el.Name != "")));

            Assert.IsTrue((from evs in AmigaEventsList.Events
                           from ev in evs.Events
                           select ev.Name).Unique());

            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.Select(el => el.DisplayName).Unique()));
            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.All(el => el.DisplayName == el.DisplayName.Trim())));
            Assert.IsTrue(AmigaEventsList.Events.All(
                aies => aies.Events.All(el => el.DisplayName != "")));

            Assert.AreEqual((from events in AmigaEventsList.Events
                             from ev in events.Events
                             select ev).Count(), 152);
        }

        [TestCategory("Filters"), TestCategory("Configuration"), TestCategory("Host"), TestMethod]
        public void Software_Filters_Test()
        {
            Assert.IsTrue(SoftwareFilters.Filters.Count() == 5);
            Assert.IsTrue(SoftwareFilters.Filters.Select(sf => sf.Name.ToLower()).Unique());
        }

        [TestCategory("Filters"), TestCategory("Configuration"), TestMethod]
        public void Filters_Test()
        {
            Assert.IsTrue(Filters.List.Count() == 35);
            Assert.IsTrue(Filters.List.Select(f => f.DisplayName.ToLower()).Unique());
        }

        [TestCategory("Filters"), TestCategory("Configuration"), TestCategory("Host"), TestMethod]
        public void Shaders_Test()
        {
            Assert.IsTrue(Shaders.List.Count() == 33);
            Assert.IsTrue(Shaders.List.Count(sh => sh.Include) == 2);
            Assert.IsTrue(Shaders.List.Count(sh => sh.Internal) == 1);
            Assert.IsTrue(Shaders.List.Count(sh => sh.Include && sh.Internal) == 0);

            foreach (var shader in Shaders.List)
            {
                Assert.IsTrue(shader.GetCode() != null);

                if (shader.Include)
                    Assert.IsTrue(shader.FileName == shader.FileName.ToLower());
            }

            Assert.IsTrue(Shaders.List.Select(sh => sh.Name.ToLower()).Unique());
            Assert.IsTrue(Shaders.List.Select(sh => sh.FileName.ToLower()).Unique());
        }

        private void CompareEventsBinding(EventBinding a_b1, EventBinding a_b2)
        {
            Assert.IsTrue(!Object.ReferenceEquals(a_b1, a_b2));
            Assert.IsTrue(a_b1.ReadOnly);
            Assert.IsTrue(!a_b2.ReadOnly);

            Assert.IsTrue(Object.ReferenceEquals(a_b1.AmigaEvent, a_b2.AmigaEvent));

            Assert.IsTrue(a_b1.Combinations.Count() == a_b2.Combinations.Count());

            foreach (var pair in a_b1.Combinations.Zip(
                a_b2.Combinations, (b1, b2) => new { el1 = b1, el2 = b2 }))
            {
                CompareHostEventsCombination(pair.el1, pair.el2);
            }
        }

        private void CompareHostEventsCombination(HostEventsCombination a_c1,
            HostEventsCombination a_c2)
        {
            Assert.IsTrue(!Object.ReferenceEquals(a_c1, a_c2));
            Assert.IsTrue(a_c1.ReadOnly);
            Assert.IsTrue(!a_c2.ReadOnly);

            Assert.IsTrue(a_c1.Combination.Count() == a_c2.Combination.Count());

            foreach (var pair in a_c1.Combination.Zip(
                a_c2.Combination, (b1, b2) => new { el1 = b1, el2 = b2 }))
            {
                Assert.IsTrue(Object.ReferenceEquals(pair.el1, pair.el2));
            }
        }

        [TestCategory("Configuration"), TestMethod]
        public void EventsBindingsList_Test()
        {
            // Clear list.
            EventsBindingList.List.Where(b => !b.ReadOnly).ToList().ForEach(
                b => EventsBindingList.Remove(b));

            Assert.IsTrue(EventsBindingList.List.Exact(DefaultEventsBindings.List));

            //Add.

            Assert.IsTrue(EventsBindingList.Add(null, DefaultEventsBindings.List.First()) == null);
            Assert.IsTrue(EventsBindingList.Add("", DefaultEventsBindings.List.First()) == null);
            Assert.IsTrue(EventsBindingList.Add(DefaultEventsBindings.List.Last().Name,
                EventsBindingList.List.First()) == null);
            Assert.IsTrue(EventsBindingList.Add(" " + DefaultEventsBindings.List.Last().Name + " ",
                DefaultEventsBindings.List.First()) == null);

            var b1 = EventsBindingList.Add(DefaultEventsBindings.List.Last().Name.ToLower() + " #1",
                DefaultEventsBindings.List.Last());
            Assert.IsTrue(b1 != null);
            Assert.IsFalse(DefaultEventsBindings.List.Contains(b1));
            Assert.IsTrue(!b1.ReadOnly);
            Assert.IsTrue(b1.Name == DefaultEventsBindings.List.Last().Name.ToLower() + " #1");

            Assert.IsTrue(EventsBindingList.List.Count() == DefaultEventsBindings.List.Count() + 1);

            var b2 = DefaultEventsBindings.List.Last();
            Assert.IsFalse(Object.ReferenceEquals(b1, b2));

            Assert.IsTrue(b1.Guid != b2.Guid);
            Assert.IsTrue(b1.Bindings.Count() == b2.Bindings.Count());
            foreach (var pair in b2.Bindings.Zip(
                b1.Bindings, (bb1, bb2) => new { el1 = bb1, el2 = bb2 }))
            {
                CompareEventsBinding(pair.el1, pair.el2);
            }

            // Remove.

            Assert.IsFalse(EventsBindingList.Remove(null));
            Assert.IsFalse(EventsBindingList.Remove(DefaultEventsBindings.List.First()));
            Assert.IsTrue(EventsBindingList.Remove(b1));

            Assert.IsTrue(EventsBindingList.List.Count() == DefaultEventsBindings.List.Count());

            // Sort.

            b1 = EventsBindingList.Add("aaaa", DefaultEventsBindings.List.Last());
            Assert.IsTrue(EventsBindingList.List.IndexOf(b1) == 0);
            b1 = EventsBindingList.Add("zzzz", DefaultEventsBindings.List.Last());
            Assert.IsTrue(EventsBindingList.List.IndexOf(b1) == EventsBindingList.List.Count() - 1);

            // Restore.
            EventsBindingList.Reload();

            EventsBindingsList_Check();
        }

        private void EventsBindingsList_Check()
        {
            // Sorted.
            Assert.IsTrue(EventsBindingList.List.Select(
                b => b.Name).OrderBy(s => s).SequenceEqual(
                    EventsBindingList.List.Select(b => b.Name)));

            // Some checks.
            Assert.IsTrue(EventsBindingList.List.All(b => b.Name != ""));
            Assert.IsTrue(EventsBindingList.List.All(b => b.Name != null));
            Assert.IsTrue(EventsBindingList.List.Select(b => b.Guid).Unique());
            Assert.IsTrue(EventsBindingList.List.Select(b => b.Name).Unique());
            Assert.IsTrue(EventsBindingList.List.All(b => b.Name == b.Name.Trim()));
        }

        [TestCategory("Configuration"), TestCategory("AmigaConfiguration"), TestMethod]
        public void EventsBindings_Test()
        {
            // Clear.
            EventsBindingList.List.Where(b => !b.ReadOnly).ToList().ForEach(
                b => EventsBindingList.Remove(b));

            // Add one.
            var b1 = EventsBindingList.Add(DefaultEventsBindings.List.Last().Name.ToLower() + " #1",
                DefaultEventsBindings.List.Last());

            // update combination

            var c2 = DefaultEventsBindings.List.Last().Bindings.Last();
            var copy = c2.Combinations.ToList();
            Assert.IsFalse(c2.UpdateCombination(0, HostEventsList.All.Take(1)));
            Assert.IsFalse(c2.UpdateCombination(1, HostEventsList.All.Take(1)));
            Assert.IsFalse(c2.UpdateCombination(2, HostEventsList.All.Take(1)));
            Assert.IsTrue(c2.Combinations.Count() == copy.Count);

            foreach (var pair in copy.Zip(
                c2.Combinations, (cc1, cc2) => new { el1 = cc1, el2 = cc2 }))
            {
                Assert.IsTrue(Object.ReferenceEquals(pair.el1, pair.el2));
            }

            var c1 = b1.Bindings.Last();

            Assert.IsTrue(c1.Combinations.Count() == 1);

            Assert.IsFalse(c1.UpdateCombination(-1, HostEventsList.All.Take(1)));
            Assert.IsFalse(c1.UpdateCombination(2, HostEventsList.All.Take(1)));
            Assert.IsFalse(c1.UpdateCombination(1, null));
            Assert.IsTrue(c1.Combinations.Count() == 1);

            Assert.IsTrue(c1.UpdateCombination(0, null));
            Assert.IsTrue(c1.Combinations.Count() == 0);

            Assert.IsTrue(c1.UpdateCombination(0, HostEventsList.All.Take(2)));
            Assert.IsTrue(c1.Combinations.Count() == 1);
            Assert.IsTrue(c1.Combinations.First().Combination.Count() == 2);
            Assert.IsTrue(c1.UpdateCombination(0, HostEventsList.All.Take(0)));
            Assert.IsTrue(c1.Combinations.Count() == 0);

            Assert.IsTrue(c1.UpdateCombination(0, HostEventsList.All.Take(2)));
            Assert.IsTrue(c1.UpdateCombination(1, HostEventsList.All.Take(3)));
            Assert.IsTrue(c1.Combinations.Count() == 2);
            Assert.IsTrue(c1.Combinations.ElementAt(0).Combination.Count() == 2);
            Assert.IsTrue(c1.Combinations.ElementAt(1).Combination.Count() == 3);
            Assert.IsTrue(c1.UpdateCombination(0, null));
            Assert.IsTrue(c1.Combinations.Count() == 1);
            Assert.IsTrue(c1.Combinations.ElementAt(0).Combination.Count() == 3);

            Assert.IsTrue(c1.UpdateCombination(0, HostEventsList.All.Take(5)));
            Assert.IsTrue(c1.Combinations.ElementAt(0).Combination.Count() == 4);

            Assert.IsTrue(c1.UpdateCombination(0, HostEventsList.All.Take(2).Repeat(2)));
            Assert.IsTrue(c1.Combinations.ElementAt(0).Combination.Count() == 2);
            Assert.IsTrue(c1.Combinations.ElementAt(0).Combination.SequenceEqual(
                HostEventsList.All.Take(2)));

            EventBindings_Check();
        }

        private void EventBindings_Check()
        {
            // GetAmigaEvents().

            foreach (var ebs in EventsBindingList.List)
            {
                var events = ebs.Bindings.Select(b => b.AmigaEvent).ToList();
                var todo = ebs.FindAmigaEvents().Events.Except(events);
                var tooverdo = events.Except(ebs.FindAmigaEvents().Events);

                Assert.IsTrue(ebs.GetAmigaEvents() != null);
                Assert.IsTrue(ebs.GetAmigaEvents() == ebs.FindAmigaEvents());

                Assert.IsTrue(todo.Count() == 0);
                Assert.IsTrue(tooverdo.Count() == 0);
                Assert.IsTrue(events.Exact(ebs.GetAmigaEvents().Events));
            }

            // Some checks.

            foreach (var ebs in EventsBindingList.List)
            {
                Assert.IsTrue(ebs.Bindings.All(b => b.AmigaEvent != null));
                Assert.IsTrue(ebs.Bindings.All(b => b.Combinations.All(c => c.Shortcut != null)));
                Assert.IsTrue(ebs.Bindings.All(b => b.Combinations.All(
                    c => c.Combination.All(cc => cc != null))));

                var combinations = from eb in ebs.Bindings
                                   from c in eb.Combinations
                                   select c;

                Assert.IsTrue(combinations.Unique());

                // Sort.
                Assert.IsTrue(ebs.Bindings.Select(b => b.AmigaEvent).SequenceEqual(ebs.FindAmigaEvents().Events));

                foreach (var b in ebs.Bindings)
                {
                    Assert.IsTrue(b.Combinations.Count() <= EventBinding.MAX_COMBINATIONS_COUNT);

                    foreach (var c in b.Combinations)
                    {
                        Assert.IsTrue(c.Combination.Count() <= HostEventsCombination.MAX_EVENTS_COUNT);
                        Assert.IsTrue(c.Combination.Unique());
                        Assert.IsTrue(c.Combination.All(e => e != null));
                    }
                }
            }
        }

        [TestCategory("Configuration"), TestCategory("Host"), TestMethod]
        public void DefaultEventsBindings_Test()
        {
            Assert.IsTrue(DefaultEventsBindings.List.All(b => b.ReadOnly));
            Assert.IsTrue(EventsBindingList.List.Except(b => !b.ReadOnly).ContainsExact(
                DefaultEventsBindings.List));

            foreach (var ebs in DefaultEventsBindings.List)
            {
                var events = ebs.Bindings.Select(b => b.AmigaEvent).ToList();
                var todo = ebs.FindAmigaEvents().Events.Except(events);
                var tooverdo = events.Except(ebs.FindAmigaEvents().Events);

                Assert.IsTrue(ebs.GetAmigaEvents() != null);
            }

            Assert.IsTrue(DefaultEventsBindings.List.Select(
                b => b.GetAmigaEvents().DeviceType).Distinct().Exact(
                    EnumExtensions.GetValues<AmigaDeviceType>()));

            Assert.IsTrue(DefaultEventsBindings.List.Select(b => b.Guid).Unique());
            Assert.IsTrue(DefaultEventsBindings.List.Select(b => b.Name).Unique());
            Assert.IsTrue(DefaultEventsBindings.List.All(b => b.Name == b.Name.Trim()));
            Assert.IsTrue(DefaultEventsBindings.List.All(b => b.Name != ""));

            foreach (var ebs in DefaultEventsBindings.List)
            {
                Assert.IsTrue(ebs.Bindings.All(b => b.AmigaEvent != null));
                Assert.IsTrue(ebs.Bindings.All(b => b.ReadOnly));
                Assert.IsTrue(ebs.Bindings.All(b => b.Combinations.All(c => c.Shortcut != null)));
                Assert.IsTrue(ebs.Bindings.All(b => b.Combinations.All(
                    c => c.Combination.All(cc => cc != null))));
                Assert.IsTrue(ebs.Bindings.All(b => b.Combinations.All(c => c.ReadOnly)));

                Assert.IsTrue(ebs.Bindings.Select(b => b.AmigaEvent).SequenceEqual(
                    ebs.FindAmigaEvents().Events));

                var combinations = from eb in ebs.Bindings
                                   from c in eb.Combinations
                                   select c;

                Assert.IsTrue(combinations.Unique());
            }

            {
                var eb = DefaultEventsBindings.List.First().Bindings.First();
                List<List<HostEvent>> list1 = (from c in eb.Combinations
                                               select (from e in c.Combination
                                                       select e).ToList()).ToList();
                Assert.IsFalse(eb.UpdateCombination(0, HostEventsList.All.Take(1)));
                List<List<HostEvent>> list2 = (from c in eb.Combinations
                                               select (from e in c.Combination
                                                       select e).ToList()).ToList();

                Assert.IsTrue(list1.Count == list1.Count);
                Assert.IsTrue(list1.Select(li => li.Count).SequenceEqual(
                    list2.Select(li => li.Count)));
                Assert.IsTrue(list1.SelectMany().SequenceEqual(list2.SelectMany()));
            }
        }

        [TestCategory("Configuration"), TestCategory("Host"), TestMethod]
        public void HostEvents_Test()
        {
            Assert.IsTrue(HostEventsList.KeyboardEvents.Events.Count() == 254);
            Assert.IsTrue(HostEventsList.KeyboardEvents.Events.Select(e => e.ID).Unique());

            Assert.IsTrue(HostEventsList.MouseEvents.Events.Count() == 40);
            Assert.IsTrue(HostEventsList.MouseEvents.Events.Select(e => e.ID).Unique());

            var x = from xx in HostEventsList.KeyboardEvents.Events
                    group xx by xx.DisplayName into gr
                    where gr.Count() > 1
                    select gr;

            Assert.IsTrue(HostEventsList.MouseEvents.Events.Select(e => e.DisplayName).Unique());
            Assert.IsTrue(HostEventsList.KeyboardEvents.Events.Select(e => e.DisplayName).Unique());

            Assert.IsTrue(HostEventsList.GetEvent("Mouse Relative Move X").PulseEvent);
            Assert.IsTrue(HostEventsList.GetEvent("Mouse Relative Move X").IgnoreWhenMapping);

            Assert.IsTrue(HostEventsList.GetEvent("Mouse Wheel Down").PulseEvent);
            Assert.IsTrue(!HostEventsList.GetEvent("Mouse Wheel Down").IgnoreWhenMapping);

            Assert.IsTrue(!HostEventsList.GetEvent("Mouse Left Button").PulseEvent);
            Assert.IsTrue(!HostEventsList.GetEvent("Mouse Left Button").IgnoreWhenMapping);

            Assert.IsTrue(HostEventsList.All.Select(e => e.DisplayName).All(s => s == s.Trim()));
            Assert.IsTrue(HostEventsList.All.Select(e => e.DisplayName).All(s => s != ""));
            Assert.IsTrue(HostEventsList.All.Select(e => e.Name).All(s => s == s.Trim()));
            Assert.IsTrue(HostEventsList.All.Select(e => e.Name).All(s => s != ""));
            Assert.IsTrue(HostEventsList.All.Select(e => e.Name).Unique());
            Assert.IsTrue(HostEventsList.All.Select(e => e.ID).All(id => id > 0));

            Assert.IsTrue(HostEventsList.All.Count() == 294);
        }

        [TestCategory("Configuration"), TestMethod]
        public void FloppySounds_Test()
        {
            foreach (var snd_list in FloppySoundsList.List)
            {
                foreach (var snd in snd_list.Sounds)
                {
                    Assert.IsTrue(snd.Name != "");

                    if (snd.Exists)
                    {
                        Assert.IsTrue(snd.GetBytes() != null);
                        var stream = snd.GetStream();
                        Assert.IsTrue(stream != null);
                        stream.Dispose();
                        Assert.IsTrue(snd.Path != "");
                    }
                    else
                    {
                        Assert.IsTrue(snd.GetBytes() == null);
                        Assert.IsTrue(snd.GetStream() == null);
                        Assert.IsTrue(snd.Path == "");
                    }
                }
            }

            Assert.IsTrue((from fs in FloppySoundsList.List
                           from snd in fs.Sounds
                           group snd.Name by snd.Name into gr
                           select gr.Count()).All(x => x == 5));

            Assert.IsTrue((from fs in FloppySoundsList.List
                           select fs.Sounds.Count()).All(x => x == 5));

            Assert.IsTrue(FloppySoundsList.List.ElementAt(0).Sounds.Count(s => s.Path != "") == 4);
            Assert.IsTrue(FloppySoundsList.List.ElementAt(0).SpinNoDisk.Path == "");
            Assert.IsTrue(FloppySoundsList.List.ElementAt(1).Sounds.Count(s => s.Path != "") == 5);

            using (var stream = StreamExtensions.FromResource(typeof(FloppySoundsList),
                "FloppySoundsList.xml"))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(stream);

                Assert.IsTrue(
                    doc.SelectNodes("FloppySoundsList//FloppySounds").Cast<XmlNode>().Select(
                        n => n.Attributes["Name"].Value).Unique());

                foreach (var fs in doc.SelectNodes("FloppySoundsList//FloppySounds").Cast<XmlNode>())
                {
                    Assert.IsTrue(fs.SelectNodes("FloppySound").Cast<XmlNode>().Select(
                        n => n.InnerText).All(n => n != ""));
                    Assert.IsTrue(fs.SelectNodes("FloppySound").Cast<XmlNode>().Select(
                        n => n.Attributes["Name"].Value).Unique());
                }
            }
        }

        [TestCategory("Base"), TestMethod]
        public void Addresses_Test()
        {
            Assert.IsTrue(MemoryHelpers.IsBankAllign(0));
            Assert.IsFalse(MemoryHelpers.IsBankAllign(1));
            Assert.IsFalse(MemoryHelpers.IsBankAllign(0xFFFF));
            Assert.IsTrue(MemoryHelpers.IsBankAllign(0x10000));
            Assert.IsFalse(MemoryHelpers.IsBankAllign(0x10001));

            Assert.IsTrue(MemoryHelpers.IsLongAllign(0));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(1));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(2));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(3));
            Assert.IsTrue(MemoryHelpers.IsLongAllign(4));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(5));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(63));
            Assert.IsTrue(MemoryHelpers.IsLongAllign(64));
            Assert.IsTrue(!MemoryHelpers.IsLongAllign(65));

            Assert.IsTrue(MemoryHelpers.IsWordAllign(0));
            Assert.IsTrue(!MemoryHelpers.IsWordAllign(1));
            Assert.IsTrue(MemoryHelpers.IsWordAllign(2));
            Assert.IsTrue(!MemoryHelpers.IsWordAllign(3));
            Assert.IsTrue(!MemoryHelpers.IsWordAllign(63));
            Assert.IsTrue(MemoryHelpers.IsWordAllign(64));
            Assert.IsTrue(!MemoryHelpers.IsWordAllign(65));
        }

        [TestCategory("Base"), TestMethod]
        public void AmigaBytes_Test()
        {
            Assert.IsTrue(MemoryHelpers.GetHighByte(0x55CD) == 0x55);
            Assert.IsTrue(MemoryHelpers.GetHighByte(0xFF00) == 0xFF);
            Assert.IsTrue(MemoryHelpers.GetHighByte(0x00FF) == 0x00);

            Assert.IsTrue(MemoryHelpers.GetLowByte(0xCD55) == 0x55);
            Assert.IsTrue(MemoryHelpers.GetLowByte(0xFF00) == 0x00);
            Assert.IsTrue(MemoryHelpers.GetLowByte(0x00FF) == 0xFF);

            Assert.IsTrue(MemoryHelpers.GetHighWord(0x55CDEFAB) == 0x55CD);
            Assert.IsTrue(MemoryHelpers.GetHighWord(0xFF000000) == 0xFF00);
            Assert.IsTrue(MemoryHelpers.GetHighWord(0x00FF0000) == 0x00FF);
            Assert.IsTrue(MemoryHelpers.GetHighWord(0x0000FFFF) == 0x0000);
            Assert.IsTrue(MemoryHelpers.GetHighWord(0x1234FFFF) == 0x1234);

            Assert.IsTrue(MemoryHelpers.GetLowWord(0x55CDEFAB) == 0xEFAB);
            Assert.IsTrue(MemoryHelpers.GetLowWord(0x0000FF00) == 0xFF00);
            Assert.IsTrue(MemoryHelpers.GetLowWord(0x000000FF) == 0x00FF);
            Assert.IsTrue(MemoryHelpers.GetLowWord(0xFFFF0000) == 0x0000);
            Assert.IsTrue(MemoryHelpers.GetLowWord(0xFFFF1234) == 0x1234);

            Assert.IsTrue(MemoryHelpers.MakeLong(0x0000, 0xFFFF) == 0x0000FFFF);
            Assert.IsTrue(MemoryHelpers.MakeLong(0xFFFF, 0x0000) == 0xFFFF0000);
            Assert.IsTrue(MemoryHelpers.MakeLong(0x1234, 0x5678) == 0x12345678);

            Assert.IsTrue(MemoryHelpers.MakeWord(0x00, 0xFF) == 0x00FF);
            Assert.IsTrue(MemoryHelpers.MakeWord(0xFF, 0x00) == 0xFF00);
            Assert.IsTrue(MemoryHelpers.MakeWord(0x12, 0x34) == 0x1234);

            ushort word = 0xFFFF;
            MemoryHelpers.SetHighByte(ref word, 0x12);
            Assert.IsTrue(word == 0x12FF);

            word = 0x0000;
            MemoryHelpers.SetHighByte(ref word, 0x12);
            Assert.IsTrue(word == 0x1200);

            word = 0xFFFF;
            MemoryHelpers.SetLowByte(ref word, 0x12);
            Assert.IsTrue(word == 0xFF12);

            word = 0x0000;
            MemoryHelpers.SetLowByte(ref word, 0x12);
            Assert.IsTrue(word == 0x0012);

            Assert.IsTrue(MemoryHelpers.SetHighByte(0xFFFF, 0x12) == 0x12FF);
            Assert.IsTrue(MemoryHelpers.SetHighByte(0x0000, 0x12) == 0x1200);
            Assert.IsTrue(MemoryHelpers.SetLowByte(0xFFFF, 0x12) == 0xFF12);
            Assert.IsTrue(MemoryHelpers.SetLowByte(0x0000, 0x12) == 0x0012);

            //

            uint lng = 0xFFFFFFFF;
            MemoryHelpers.SetHighWord(ref lng, 0x1234);
            Assert.IsTrue(lng == 0x1234FFFF);

            lng = 0x00000000;
            MemoryHelpers.SetHighWord(ref lng, 0x1234);
            Assert.IsTrue(lng == 0x12340000);

            lng = 0xFFFFFFFF;
            MemoryHelpers.SetLowWord(ref lng, 0x1234);
            Assert.IsTrue(lng == 0xFFFF1234);

            lng = 0x00000000;
            MemoryHelpers.SetLowWord(ref lng, 0x1234);
            Assert.IsTrue(lng == 0x00001234);

            Assert.IsTrue(MemoryHelpers.SetHighWord(0xFFFFFFFF, 0x1234) == 0x1234FFFF);
            Assert.IsTrue(MemoryHelpers.SetHighWord(0x00000000, 0x1234) == 0x12340000);
            Assert.IsTrue(MemoryHelpers.SetLowWord(0xFFFFFFFF, 0x1234) == 0xFFFF1234);
            Assert.IsTrue(MemoryHelpers.SetLowWord(0x00000000, 0x1234) == 0x00001234);
        }

        [TestCategory("Memory"), TestCategory("AmigaConfiguration"), TestMethod]
        public void Memory_MemoryMap_Test()
        {
            foreach (var def_config in DefaultAmigaConfigs.Configs)
            {
                AmigaConfig config = def_config.GetConfigCopy();

                MemoryMap mem_map = MemoryMaps.GetMap(config.AmigaModel);

                Machine machine = null;

                try
                {
                    machine = new Machine(config);
                }
                catch (RomLoadException)
                {
                    RomLoadExceptionValid(config);
                    continue;
                }
                catch (CPUInitializationException)
                {
                    CPUIniitalizationExceptionValid(config);
                    continue;
                }

                List<MemoryBank> banks = new List<MemoryBank>();

                if (config.Memory.ChipMemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Chip);
                    banks.AddRange(CheckBanks(machine, chunk.Address, config.Memory.ChipMemorySize,
                        chunk.Type));
                }
                if (config.Memory.FastMemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Fast);
                    banks.AddRange(CheckBanks(machine, chunk.Address, config.Memory.FastMemorySize,
                        chunk.Type));
                }
                if (config.Memory.Kickstart.Required)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Kickstart);
                    uint size = (uint)new FileInfo(config.Memory.Kickstart.FindRom()).Length;
                    banks.AddRange(CheckBanks(machine, chunk.Address + chunk.Size - size, size,
                        chunk.Type));
                }
                if (config.Memory.KickstartExtended.Required)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.KickstartExtended);
                    uint size = (uint)new FileInfo(config.Memory.KickstartExtended.FindRom()).Length;
                    banks.AddRange(CheckBanks(machine, chunk.Address + chunk.Size - size, size,
                        chunk.Type));
                }
                if (config.Memory.MotherboardMemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Motherboard);
                    banks.AddRange(CheckBanks(machine, chunk.Address + chunk.Size -
                        config.Memory.MotherboardMemorySize, config.Memory.MotherboardMemorySize,
                        chunk.Type));
                }
                if (config.Memory.ProcessorSlotMemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.ProcessorSlot);
                    banks.AddRange(CheckBanks(machine, chunk.Address,
                        config.Memory.ProcessorSlotMemorySize, chunk.Type));
                }
                if (config.Memory.SlowMemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Slow);
                    banks.AddRange(CheckBanks(machine, chunk.Address,
                        config.Memory.SlowMemorySize, chunk.Type));
                }
                if (config.Memory.Z3MemorySize != 0)
                {
                    var chunk = mem_map.Chunks.First(ch => ch.Type == MemoryBankType.Z3);
                    banks.AddRange(CheckBanks(machine, chunk.Address,
                        config.Memory.Z3MemorySize, chunk.Type));
                }

                Assert.IsTrue(banks.Exact(machine.Memory.GetValidBanks()));
            }
        }

        [TestCategory("Memory"), TestCategory("AmigaConfiguration"), TestMethod]
        public void Default_MemoryMap_DefaultConfigs_Test()
        {
            foreach (var def_config in DefaultAmigaConfigs.Configs)
            {
                AmigaConfig config = def_config.GetConfigCopy();

                Machine machine;

                try
                {
                    machine = new Machine(config);
                }
                catch (RomLoadException)
                {
                    RomLoadExceptionValid(config);
                    continue;
                }
                catch (CPUInitializationException)
                {
                    CPUIniitalizationExceptionValid(config);
                    continue;
                }

                MemoryMap mem_map = MemoryMaps.GetMap(config.AmigaModel);

                if (config.Memory.ChipMemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Chip));
                if (config.Memory.FastMemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Fast));
                if (config.Memory.Kickstart.Required)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Kickstart));
                if (config.Memory.KickstartExtended.Required)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.KickstartExtended));
                if (config.Memory.MotherboardMemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Motherboard));
                if (config.Memory.ProcessorSlotMemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.ProcessorSlot));
                if (config.Memory.SlowMemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Slow));
                if (config.Memory.Z3MemorySize != 0)
                    Assert.IsTrue(mem_map.Chunks.Any(chunk => chunk.Type == MemoryBankType.Z3));
            }
        }

        [TestCategory("Amiga"), TestMethod]
        public void Keyboard_RawCodes_Test()
        {
            Assert.IsTrue(RawCodesList.List.Count() == 5);

            var models = RawCodesList.List.Select(rcs => rcs.AmigaModel);

            Assert.IsTrue(models.All(model => model != AmigaModel.Custom));

            var flags = EnumExtensions.GetValues<AmigaModel>().Except(AmigaModel.Custom);

            var models_flags = from m1 in flags
                               from m2 in models
                               where (m1 & m2) != 0
                               select m1;

            Assert.IsTrue(flags.Exact(models_flags));

            Assert.IsTrue(RawCodesList.List.All(
                raw_codes => raw_codes.List.All(
                    raw_code => raw_code.AmigaEvent != null)));

            Assert.IsTrue(RawCodesList.List.All(
                raw_codes => raw_codes.List.Select(
                    raw_code => raw_code.AmigaEvent).Unique()));

            var events = AmigaEventsList.Events.First(
                evs => evs.DeviceType == AmigaDeviceType.Keyboard).Events;

            Assert.IsTrue(RawCodesList.List.All(
                raw_codes => raw_codes.List.All(
                    raw_code => events.Contains(raw_code.AmigaEvent))));

            Assert.IsTrue((from rcs in RawCodesList.List
                           from rc in rcs.List
                           select rc.AmigaEvent).Distinct().Exact(events));

            Assert.IsTrue((from rcs in RawCodesList.List
                           from rc in rcs.List
                           group rc.AmigaEvent by rc.ScanCode into gr
                           select gr.Distinct().Count()).All(gr => gr == 1));

            Assert.IsTrue((from rcs in RawCodesList.List
                           from rc in rcs.List
                           group rc.ScanCode by rc.AmigaEvent into gr
                           select gr.Distinct().Count()).All(gr => gr == 1));

            Assert.IsTrue(RawCodesList.List.All(
                raw_codes => raw_codes.List.Select(
                    raw_code => raw_code.ScanCode).Unique()));
        }

        [TestCategory("Memory"), TestMethod]
        public void Roms_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(AmigaModel.A500).First().GetConfigCopy();
            Machine machine = new Machine(config);

            var chunk = GetFirstBank(MemoryBankType.Kickstart, machine.Memory);

            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000000) == 0x11114EF9);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000040) == 0x20524F4D);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x0003FFFC) == 0x001E001F);

            config = DefaultAmigaConfigs.GetConfigs(AmigaModel.CD32).First().GetConfigCopy();
            machine = new Machine(config);

            chunk = GetFirstBank(MemoryBankType.Kickstart, machine.Memory);

            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000000) == 0x11144EF9);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000040) == 0x7300436F);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x0007FFFC) == 0x001E001F);

            chunk = GetFirstBank(MemoryBankType.KickstartExtended, machine.Memory);

            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000000) == 0x11144EF9);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000040) == 0x65006368);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x0007FFFC) == 0x001E001F);

            config = DefaultAmigaConfigs.GetConfigs(AmigaModel.A1000).First().GetConfigCopy();
            machine = new Machine(config);

            chunk = GetFirstBank(MemoryBankType.Kickstart, machine.Memory);

            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000000) == 0x11114EF9);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00000040) == 0x70797269);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00001FFC) == 0xFFFFFFFF);
            Assert.IsTrue(machine.Memory.GetLong(chunk.Address + 0x00002000) == 0xFFFFFFFF);
        }

        [TestCategory("Memory"), TestMethod]
        public void Memory_Test()
        {
            AmigaConfig config = DefaultAmigaConfigs.GetConfigs(AmigaModel.A3000).ElementAt(1).GetConfigCopy();
            IsException(() => new Machine(config));

            config = DefaultAmigaConfigs.GetConfigs(AmigaModel.A500).First().GetConfigCopy();
            Machine machine = new Machine(config);

            for (uint i = 0; i < Consts.BANK_SIZE; i++)
                Assert.IsTrue(machine.Memory.GetByte(i) == Consts.RESET_BYTE_VALUE);

            machine.Memory.SetWord(0x10001234, 0x2345);
            Assert.IsTrue(machine.Memory.GetWord(0x00001234) == 0x2345);

            machine.Memory.SetWord(0x00300000, 0x1234);
            Assert.IsTrue(machine.Memory.GetWord(0x00300000) == Consts.RESET_WORD_VALUE);

            // Fill bytes.

            machine.Memory.FillBytes(0x00C00001, 0x10 - 1, 0x45);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00000) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00001) == 0x45);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00009) == 0x45);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00010) == 0xFF);

            for (uint addr = 0x00C00001; addr < 0x00C00001 + 0x10 - 1; addr++)
                Assert.IsTrue(machine.Memory.GetByte(addr) == 0x45);

            machine.Memory.FillBytes(0x00C00102, 0x10 - 1, 0x46);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00101) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00102) == 0x46);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00110) == 0x46);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00111) == 0xFF);

            machine.Memory.FillBytes(0x00C00203, 0x10 - 1, 0x47);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00202) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00203) == 0x47);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00211) == 0x47);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00212) == 0xFF);

            machine.Memory.FillBytes(0x00C00304, 0x10 - 1, 0x48);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00303) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00304) == 0x48);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00312) == 0x48);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00313) == 0xFF);

            machine.Memory.FillBytes(0x00C00404, 1, 0x49);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00403) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00404) == 0x49);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00405) == 0xFF);

            machine.Memory.FillBytes(0x00C00554, 0, 0x60);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00553) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00554) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00555) == 0xFF);

            // Fill words.

            machine.Memory.FillWords(0x00C00502, 0x10 - 2, 0x4A4A);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00501) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00502) == 0x4A4A);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00510) == 0xFF);

            for (uint addr = 0x00C00502; addr < 0x00C00502 + 0x10 - 2; addr += 2)
                Assert.IsTrue(machine.Memory.GetWord(addr) == 0x4A4A);

            machine.Memory.FillWords(0x00C00604, 0x10 - 2, 0x4B4B);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00603) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00604) == 0x4B4B);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00612) == 0xFF);

            machine.Memory.FillWords(0x00C00704, 2, 0x4C4C);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00703) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00704) == 0x4C4C);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00706) == 0xFF);

            machine.Memory.FillWords(0x00C00806, 2, 0x4D4D);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00805) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00806) == 0x4D4D);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00808) == 0xFF);

            machine.Memory.FillWords(0x00C00856, 0, 0x6D6D);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00854) == 0xFFFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00856) == 0xFFFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C00858) == 0xFFFF);

            IsException(() => machine.Memory.FillWords(0x00C00808, 1, 0xDDDD));
            IsException(() => machine.Memory.FillWords(0x00C00808, 3, 0xDDDD));
            IsException(() => machine.Memory.FillWords(0x00C00807, 2, 0xDDDD));

            // Fill longs.

            machine.Memory.FillLongs(0x00C00904, 0x10 - 4, 0x4E4E4E4E);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00903) == 0xFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00904) == 0x4E4E4E4E);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00914) == 0xFF);

            for (uint addr = 0x00C00904; addr < 0x00C00904 + 0x10 - 4; addr += 4)
                Assert.IsTrue(machine.Memory.GetLong(addr) == 0x4E4E4E4E);

            machine.Memory.FillLongs(0x00C00A04, 0x10 - 4, 0x4F4F4F4F);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00A03) == 0xFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00A04) == 0x4F4F4F4F);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00A12) == 0xFF);

            machine.Memory.FillLongs(0x00C00B04, 4, 0x50505050);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00B03) == 0xFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00B04) == 0x50505050);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00B08) == 0xFF);

            machine.Memory.FillLongs(0x00C00C04, 4, 0x51515151);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00C03) == 0xFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00C04) == 0x51515151);
            Assert.IsTrue(machine.Memory.GetByte(0x00C00C08) == 0xFF);

            machine.Memory.FillLongs(0x00C00D04, 0, 0x51515151);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00D00) == 0xFFFFFFFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00D04) == 0xFFFFFFFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C00D08) == 0xFFFFFFFF);

            IsException(() => machine.Memory.FillLongs(0x00C00808, 1, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00808, 2, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00808, 3, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00808, 5, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00808, 6, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00808, 7, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00805, 4, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00806, 4, 0xDDDD));
            IsException(() => machine.Memory.FillLongs(0x00C00807, 4, 0xDDDD));

            // Byte <-> Word <-> Long

            machine.Memory.SetByte(0x00C01906, 0x01);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01906) == 0x01);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01907) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01908) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01909) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01906) == 0x01FF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01908) == 0xFFFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C01906) == 0x01FFFFFF);

            machine.Memory.SetByte(0x00C01907, 0x02);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01906) == 0x01);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01907) == 0x02);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01908) == 0xFF);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01909) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01906) == 0x0102);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01908) == 0xFFFF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C01906) == 0x0102FFFF);

            machine.Memory.SetByte(0x00C01908, 0x03);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01906) == 0x01);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01907) == 0x02);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01908) == 0x03);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01909) == 0xFF);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01906) == 0x0102);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01908) == 0x03FF);
            Assert.IsTrue(machine.Memory.GetLong(0x00C01906) == 0x010203FF);

            machine.Memory.SetByte(0x00C01909, 0x04);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01906) == 0x01);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01907) == 0x02);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01908) == 0x03);
            Assert.IsTrue(machine.Memory.GetByte(0x00C01909) == 0x04);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01906) == 0x0102);
            Assert.IsTrue(machine.Memory.GetWord(0x00C01908) == 0x0304);
            Assert.IsTrue(machine.Memory.GetLong(0x00C01906) == 0x01020304);
        }

        [TestCategory("Memory"), TestMethod]
        public void DefaultAmigaMaps_Test()
        {
            var maps = (from def_map in MemoryMaps.Maps
                        orderby def_map.AmigaModel.ToString()
                        select def_map.AmigaModel).Distinct();

            Assert.IsTrue(EnumExtensions.GetValues<AmigaModel>().Except(AmigaModel.Custom).
                OrderBy(m => m.ToString()).SequenceEqual(maps));

            foreach (var map in MemoryMaps.Maps)
            {
                for (int i = 0; i < map.Chunks.Count() - 1; i++)
                {
                    MemoryMapChunk map_chunk_1 = map[i];
                    MemoryMapChunk map_chunk_2 = map[i + 1];

                    Assert.IsTrue(map_chunk_1.LastByteAddress < map_chunk_2.Address);
                    Assert.IsTrue(map_chunk_1.Size > 0);
                    Assert.IsTrue(map_chunk_2.Size > 0);
                }

                foreach (var chunk in map.Chunks)
                {
                    Assert.IsTrue(MemoryHelpers.IsBankAllign(chunk.Address));
                    Assert.IsTrue(MemoryHelpers.IsBankAllign(chunk.Size));
                }

                Assert.IsTrue(map.Chunks.Select(m => m.Type).Unique());
            }
        }

        [TestCategory("AmigaConfiguration"), TestMethod]
        public void Default_AmigaConfigurations_Test()
        {
            Assert.IsTrue(DefaultAmigaConfigs.Configs.Select(d => d.Description).All(d => d != null));
            Assert.IsTrue(DefaultAmigaConfigs.Configs.Select(d => d.Description).All(d => d != ""));
            Assert.IsTrue((from d in DefaultAmigaConfigs.Configs
                           group d by d.AmigaModel into g
                           select (g.Count() == g.Distinct().Count())).All(b => b));

            Assert.IsTrue(DefaultAmigaConfigs.Configs.Select(
                d => d.AmigaModel).All(m => m != AmigaModel.Custom));

            Assert.IsTrue(DefaultAmigaConfigs.Configs.Select(
                d => d.GetConfigCopy()).All(c => c != null));
            Assert.IsTrue(DefaultAmigaConfigs.Configs.All(
                d => d.GetConfigCopy().AmigaModel == d.AmigaModel));
            Assert.IsTrue(DefaultAmigaConfigs.Configs.Select(
                d => d.GetConfigCopy()).Distinct().Count() ==
                DefaultAmigaConfigs.Configs.Count());

            foreach (var def_config in DefaultAmigaConfigs.Configs)
            {
                AmigaConfig config = def_config.GetConfigCopy();

                try
                {
                    new Machine(config);
                }
                catch (RomLoadException)
                {
                    RomLoadExceptionValid(config);
                    continue;
                }
                catch (CPUInitializationException)
                {
                    CPUIniitalizationExceptionValid(config);
                    continue;
                }
            }

            var cfgs = from def_config in DefaultAmigaConfigs.Configs
                       group def_config.ShortDescription by new
                       {
                           rom = def_config.GetConfigCopy().Memory.Kickstart.RomType,
                           rom_ext = def_config.GetConfigCopy().Memory.KickstartExtended.RomType
                       } into g
                       select g.Distinct();

            Assert.IsTrue(cfgs.All(g => g.Count() == 1));
            Assert.IsTrue(cfgs.SelectMany().Distinct().Count() == cfgs.Count());

            foreach (var def_config in DefaultAmigaConfigs.Configs)
            {
                Assert.IsTrue(def_config.GetConfigCopy().FileName == "");
                Assert.IsTrue(def_config.GetConfigCopy().Memory.FlashPath == "");

                Assert.IsTrue(def_config.GetConfigCopy().Memory.Kickstart.FilePath == "");
                Assert.IsTrue(def_config.GetConfigCopy().Memory.Kickstart.SHA1 == "");
                Assert.IsTrue(def_config.GetConfigCopy().Memory.Kickstart.RomType != RomType.Undefined);

                Assert.IsTrue(def_config.GetConfigCopy().Memory.KickstartExtended.FilePath == "");
                Assert.IsTrue(def_config.GetConfigCopy().Memory.KickstartExtended.SHA1 == "");

                EventsBinding[] ar = new EventsBinding[] {
                    def_config.GetConfigCopy().ParallelJoystick1.EventsBinding, 
                    def_config.GetConfigCopy().ParallelJoystick2.EventsBinding, 
                    def_config.GetConfigCopy().Joyport1.EventsBinding, 
                    def_config.GetConfigCopy().Joyport2.EventsBinding, 
                    def_config.GetConfigCopy().Input.CDTVFrontPanelAndPilotEventsBinding, 
                    def_config.GetConfigCopy().Input.KeyboardEventsBinding, 
                    def_config.GetConfigCopy().Input.ShortcutsEventsBinding };

                ar.All(b => DefaultEventsBindings.List.Contains(b));
            }
        }

        [TestCategory("Base"), TestMethod]
        public void HexTest()
        {
            Random rnd = new Random();

            for (int i = 0; i < 256; i++)
            {
                byte by1 = (byte)i;

                string str1 = Hex.ByteToHex(by1, false);
                string str2 = Hex.ByteToHex(by1, true);

                Assert.AreEqual(str1, str2.RemoveFromLeft(2));

                string str3 = by1.ToString("X2");

                Assert.AreEqual(str1, str3);

                byte by2 = Hex.HexToByte(str1, false);
                byte by3 = Hex.HexToByte(str2, true);

                Assert.AreEqual(by1, by2);
                Assert.AreEqual(by2, by3);
            }

            for (int i = 0; i < 10000; i++)
            {
                ushort us1 = (ushort)rnd.Next(0, ushort.MaxValue);

                string str1 = Hex.UShortToHex(us1, false);
                string str2 = Hex.UShortToHex(us1, true);

                Assert.AreEqual(str1, str2.RemoveFromLeft(2));

                string str3 = us1.ToString("X4");

                Assert.AreEqual(str1, str3);

                ushort us2 = Hex.HexToUShort(str1, false);
                ushort us3 = Hex.HexToUShort(str2, true);

                Assert.AreEqual(us1, us2);
                Assert.AreEqual(us2, us3);
            }

            for (int i = 0; i < 10000; i++)
            {
                uint ui1 = (uint)rnd.Next();

                string str1 = Hex.UIntToHex(ui1, false);
                string str2 = Hex.UIntToHex(ui1, true);

                Assert.AreEqual(str1, str2.RemoveFromLeft(2));

                string str3 = ui1.ToString("X8");

                Assert.AreEqual(str1, str3);

                uint ui2 = Hex.HexToUInt(str1, false);
                uint ui3 = Hex.HexToUInt(str2, true);

                Assert.AreEqual(ui1, ui2);
                Assert.AreEqual(ui2, ui3);
            }

            for (int i = 0; i < 10000; i++)
            {
                byte[] bytes1 = new byte[rnd.Next(0, 20) * 2];
                rnd.NextBytes(bytes1);
                ushort[] ushorts1 = new ushort[bytes1.Length / 2];
                Buffer.BlockCopy(bytes1, 0, ushorts1, 0, bytes1.Length);

                string str1 = MemoryHelpers.AmigaMemoryToHex(ushorts1);
                SwapBytes(bytes1);
                string str2 = BitConverter.ToString(bytes1).Replace("-", "");
                SwapBytes(bytes1);

                Assert.AreEqual(str1, str2);

                int from = rnd.Next(0, ushorts1.Length);
                int size = rnd.Next(0, ushorts1.Length - from + 1);

                string str3 = MemoryHelpers.AmigaMemoryToHex(ushorts1, (uint)from, (uint)size);
                SwapBytes(bytes1);
                string str4 = BitConverter.ToString(bytes1, from * 2, size * 2).Replace("-", "");
                SwapBytes(bytes1);

                Assert.AreEqual(str3, str4);

                ushort[] ushorts2 = MemoryHelpers.HexToAmigaMemory(str1);
                CollectionAssert.AreEqual(ushorts1, ushorts2);

                MemoryHelpers.HexToAmigaMemory(str3, ushorts2, (uint)from);
                CollectionAssert.AreEqual(ushorts1, ushorts2);
            }
        }

        [TestCategory("Configuration"), TestCategory("Audio"), TestMethod]
        public void AmigaAudioBleps_Test()
        {
            var list = AmigaAudioBleps.List;

            Assert.AreEqual(84, list.Count);

            Assert.IsTrue((from blep in list
                           select blep.Blep.Count).All(c => c == AmigaAudioBlep.AUDIO_BLEP_LENGTH));

            Assert.IsTrue((from blep in list
                           select blep.SamplingRate).All(sr => sr == HostAudioSamplingRate.FQ_44100));

            Assert.IsTrue(list.Select(bleps => bleps.Name).All(s => !String.IsNullOrWhiteSpace(s)));

            var gr_by_name = from blep in list
                             group blep by blep.Name into gr
                             select gr;

            Assert.AreEqual(14, gr_by_name.Count());

            foreach (var name_gr in gr_by_name)
            {
                Assert.AreEqual(6, name_gr.Count());
                Assert.IsTrue(name_gr.Select(x => x.Model).Distinct().Count() == 1);

                var gr_mode = from blep in name_gr
                              group blep by blep.TVMode into gr
                              select gr;

                Assert.AreEqual(2, gr_mode.Count());

                foreach (var mode_gr in gr_mode)
                {
                    Assert.AreEqual(3, mode_gr.Count());
                    Assert.IsTrue(name_gr.Select(x => x.Filter).Distinct().Count() == 3);
                }
            }

            {
                var blep = AmigaAudioBleps.Find("A500 R6a7", TVMode.PAL, HostAudioSamplingRate.FQ_44100, 
                    AmigaAudioBlepFilterType.LedOn);

                Assert.AreEqual(-0.999664130175, blep.Blep[0]);
                Assert.AreEqual(-0.999615961101, blep.Blep[111]);
                Assert.AreEqual(-1.27283048061E-15, blep.Blep[2047]);
            }

            {
                var blep = AmigaAudioBleps.Find("A500 R6a7", TVMode.PAL, HostAudioSamplingRate.FQ_44100,
                    AmigaAudioBlepFilterType.LedOff);

                Assert.AreEqual(-0.999999240241, blep.Blep[0]);
                Assert.AreEqual(-0.993689999153, blep.Blep[111]);
                Assert.AreEqual(1.54414580259E-15, blep.Blep[2047]);
            }

            {
                var blep = AmigaAudioBleps.Find("A500 R6a7", TVMode.PAL, HostAudioSamplingRate.FQ_44100,
                    AmigaAudioBlepFilterType.Unfiltered);

                Assert.AreEqual(-0.999938503722, blep.Blep[0]);
                Assert.AreEqual(-0.955446036521, blep.Blep[111]);
                Assert.AreEqual(-8.55150911021E-16, blep.Blep[2047]);
            }

            {
                var blep = AmigaAudioBleps.Find("A1200", TVMode.NTSC, HostAudioSamplingRate.FQ_44100,
                    AmigaAudioBlepFilterType.LedOn);

                Assert.AreEqual(-0.999339510562, blep.Blep[0]);
                Assert.AreEqual(-0.999260337982, blep.Blep[111]);
                Assert.AreEqual(-2.79096307917E-15, blep.Blep[2047]);
            }

            {
                var blep = AmigaAudioBleps.Find("A1200", TVMode.NTSC, HostAudioSamplingRate.FQ_44100,
                    AmigaAudioBlepFilterType.LedOff);

                Assert.AreEqual(-0.999999456317, blep.Blep[0]);
                Assert.AreEqual(-0.988746551215, blep.Blep[111]);
                Assert.AreEqual(-4.54516645343E-16, blep.Blep[2047]);
            }

            {
                var blep = AmigaAudioBleps.Find("A1200", TVMode.NTSC, HostAudioSamplingRate.FQ_44100,
                    AmigaAudioBlepFilterType.Unfiltered);

                Assert.AreEqual(-0.999938810815, blep.Blep[0]);
                Assert.AreEqual(-0.979578886399, blep.Blep[111]);
                Assert.AreEqual(-2.26282480167E-15, blep.Blep[2047]);
            }
        }

        [TestCategory("Base"), TestCategory("Audio"), TestMethod]
        public void EventCalculator_Test()
        {
            {
                var ec = new EventCalculator(100);
                Assert.IsTrue(ec.EventCycle == 100);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 200);
            }

            {
                var ec = new EventCalculator(100.3);
                Assert.IsTrue(ec.EventCycle == 100);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 201);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 301);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 401);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 501);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 602);
            }

            {
                var ec = new EventCalculator(100.7);
                Assert.IsTrue(ec.EventCycle == 101);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 201);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 302);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 403);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 504);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 604);
            }

            {
                var ec = new EventCalculator(100.7);
                ec.Test_Set(0.345612343556464, 4807);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 4908);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 5009);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 5109);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 5210);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 5311);
            }

            {
                var ec = new EventCalculator(100.7);
                ec.Test_Set(0.345612343556464, 9223372036854774807);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 9223372036854774908);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 9223372036854775009);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 9223372036854775109);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 9223372036854775210);
                ec.NextEventCycle();
                Assert.IsTrue(ec.EventCycle == 9223372036854775311);
            }
        }
    }
}